"""
WAL查看工具 - 用于读取和分析WAL文件
"""

import os
import sys
import json
import argparse
from typing import List, Dict, Any, Optional
from datetime import datetime

# 添加当前目录到Python路径
current_dir = os.path.dirname(os.path.abspath(__file__))
if current_dir not in sys.path:
    sys.path.insert(0, current_dir)

from wal_types import WALRecord, WALRecordType, WALConstants


class WALReader:
    """WAL文件读取器"""
    
    def __init__(self):
        self.records_cache = {}
    
    def read_wal_file(self, wal_file_path: str) -> List[Dict[str, Any]]:
        """
        读取并解析WAL文件，返回人类可读的格式
        
        Args:
            wal_file_path: WAL文件路径
            
        Returns:
            解析后的记录列表
        """
        if not os.path.exists(wal_file_path):
            raise FileNotFoundError(f"WAL文件不存在: {wal_file_path}")
        
        records = []
        
        try:
            with open(wal_file_path, 'rb') as f:
                while True:
                    # 读取记录头部
                    header_data = f.read(32)
                    if len(header_data) < 32:
                        break  # 文件结束
                    
                    # 解析头部获取记录长度
                    import struct
                    lsn, timestamp_us, total_length, record_type_val = struct.unpack('QQII', header_data[:24])
                    
                    if total_length < 32 or total_length > WALConstants.MAX_WAL_RECORD_SIZE:
                        print(f"警告: 发现异常记录长度 {total_length}，跳过")
                        break
                    
                    # 读取完整记录
                    f.seek(-32, 1)  # 回退到记录开始
                    record_data = f.read(total_length)
                    
                    if len(record_data) < total_length:
                        print(f"警告: 记录数据不完整，期望 {total_length} 字节，实际 {len(record_data)} 字节")
                        break
                    
                    try:
                        # 反序列化记录
                        wal_record = WALRecord.deserialize(record_data)
                        
                        # 验证校验和
                        if not wal_record.verify_checksum():
                            print(f"警告: LSN {lsn} 的记录校验和验证失败")
                            continue
                        
                        # 转换为人类可读格式
                        readable_record = self._convert_to_readable(wal_record)
                        records.append(readable_record)
                        
                    except Exception as e:
                        print(f"警告: 解析LSN {lsn} 记录时出错: {e}")
                        continue
        
        except Exception as e:
            print(f"读取WAL文件时出错: {e}")
            raise
        
        return records
    
    def _convert_to_readable(self, wal_record: WALRecord) -> Dict[str, Any]:
        """将WAL记录转换为人类可读格式"""
        readable = {
            'lsn': wal_record.lsn,
            'timestamp': wal_record.timestamp.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3],
            'transaction_id': wal_record.transaction_id,
            'operation': wal_record.record_type.name,
            'table_name': wal_record.table_name,
            'page_id': wal_record.page_id,
            'offset': wal_record.offset
        }
        
        # 解析before和after image
        if wal_record.before_image:
            try:
                import json
                readable['before_data'] = json.loads(wal_record.before_image.decode('utf-8'))
            except:
                readable['before_data'] = f"<{len(wal_record.before_image)} bytes>"
        
        if wal_record.after_image:
            try:
                import json
                readable['after_data'] = json.loads(wal_record.after_image.decode('utf-8'))
            except:
                readable['after_data'] = f"<{len(wal_record.after_image)} bytes>"
        
        return readable
    
    def get_wal_summary(self, database_path: str) -> Dict[str, Any]:
        """获取WAL摘要信息，返回字典"""
        wal_dir = os.path.join(database_path, WALConstants.WAL_DIRECTORY)
        
        if not os.path.exists(wal_dir):
            return {'error': 'WAL目录不存在'}
        
        try:
            # 读取控制文件
            control_file = os.path.join(wal_dir, 'wal.control')
            control_info = {}
            
            if os.path.exists(control_file):
                with open(control_file, 'r') as f:
                    control_info = json.loads(f.read())
            
            # 统计WAL文件
            wal_files = sorted([f for f in os.listdir(wal_dir) if f.endswith('.log')])
            total_size = 0
            total_records = 0
            
            for wal_file in wal_files:
                file_path = os.path.join(wal_dir, wal_file)
                file_size = os.path.getsize(file_path)
                total_size += file_size
                
                # 计算记录数
                try:
                    records = self.read_wal_file(file_path)
                    total_records += len(records)
                except:
                    pass  # 忽略读取错误的文件
            
            return {
                'database_path': database_path,
                'current_wal_file': control_info.get('current_wal_file', 'unknown'),
                'next_lsn': control_info.get('next_lsn', 0),
                'last_checkpoint_lsn': control_info.get('last_checkpoint_lsn', 0),
                'last_checkpoint_time': control_info.get('last_checkpoint_time', 'unknown'),
                'wal_files': wal_files,
                'total_files': len(wal_files),
                'total_size': total_size,
                'total_records': total_records
            }
            
        except Exception as e:
            return {'error': f'读取WAL摘要失败: {e}'}
    
    def print_wal_summary(self, database_path: str):
        """打印WAL文件摘要"""
        wal_dir = os.path.join(database_path, WALConstants.WAL_DIRECTORY)
        
        if not os.path.exists(wal_dir):
            print(f"WAL目录不存在: {wal_dir}")
            return
        
        print(f"\n=== WAL文件摘要: {database_path} ===")
        
        # 显示控制文件
        control_file = os.path.join(wal_dir, WALConstants.WAL_CONTROL_FILE)
        if os.path.exists(control_file):
            try:
                with open(control_file, 'r', encoding='utf-8') as f:
                    control_data = json.load(f)
                    print(f"当前WAL文件: {control_data.get('current_wal_file', 'unknown')}")
                    print(f"下一个LSN: {control_data.get('next_lsn', 'unknown')}")
                    print(f"最后检查点LSN: {control_data.get('last_checkpoint_lsn', 'unknown')}")
                    print(f"最后检查点时间: {control_data.get('last_checkpoint_time', 'unknown')}")
            except Exception as e:
                print(f"读取控制文件失败: {e}")
        
        # 显示检查点文件
        checkpoint_file = os.path.join(wal_dir, WALConstants.WAL_CHECKPOINT_FILE)
        if os.path.exists(checkpoint_file):
            try:
                with open(checkpoint_file, 'r', encoding='utf-8') as f:
                    checkpoint_data = json.load(f)
                    print(f"检查点LSN: {checkpoint_data.get('checkpoint_lsn', 'unknown')}")
                    print(f"检查点时间: {checkpoint_data.get('checkpoint_time', 'unknown')}")
                    print(f"活跃事务数: {len(checkpoint_data.get('active_transactions', []))}")
            except Exception as e:
                print(f"读取检查点文件失败: {e}")
        
        # 显示WAL文件列表
        wal_files = sorted([f for f in os.listdir(wal_dir) if f.endswith('.log')])
        print(f"\nWAL文件列表 ({len(wal_files)} 个文件):")
        
        total_size = 0
        for wal_file in wal_files:
            file_path = os.path.join(wal_dir, wal_file)
            file_size = os.path.getsize(file_path)
            total_size += file_size
            
            # 统计记录数
            try:
                records = self.read_wal_file(file_path)
                record_count = len(records)
            except:
                record_count = "未知"
            
            print(f"  {wal_file}: {file_size:,} 字节, {record_count} 条记录")
        
        print(f"\n总大小: {total_size:,} 字节 ({total_size/1024/1024:.2f} MB)")
    
    def print_wal_records(self, wal_file_path: str, limit: int = None, 
                         operation_filter: str = None):
        """
        打印WAL记录详情
        
        Args:
            wal_file_path: WAL文件路径
            limit: 限制显示的记录数
            operation_filter: 操作类型过滤器
        """
        try:
            records = self.read_wal_file(wal_file_path)
            
            # 应用过滤器
            if operation_filter:
                records = [r for r in records if r['operation'].upper() == operation_filter.upper()]
            
            # 应用限制
            if limit:
                records = records[-limit:]  # 显示最后N条记录
            
            print(f"\n=== WAL记录详情: {os.path.basename(wal_file_path)} ===")
            print(f"总记录数: {len(records)}")
            
            if not records:
                print("没有记录可显示")
                return
            
            for record in records:
                print(f"\nLSN: {record['lsn']} | 时间: {record['timestamp']} | 类型: {record['operation']}")
                print(f"  事务ID: {record['transaction_id']}")
                
                if record['table_name']:
                    print(f"  表名: {record['table_name']}")
                
                if record['page_id'] is not None:
                    print(f"  页面ID: {record['page_id']}")
                
                if 'before_data' in record:
                    print(f"  修改前: {record['before_data']}")
                
                if 'after_data' in record:
                    print(f"  修改后: {record['after_data']}")
            
            print("=" * 60)
            
        except Exception as e:
            print(f"读取WAL记录时出错: {e}")
    
    def analyze_transactions(self, database_path: str) -> Dict[str, Any]:
        """分析事务统计信息"""
        wal_dir = os.path.join(database_path, WALConstants.WAL_DIRECTORY)
        
        if not os.path.exists(wal_dir):
            return {'error': 'WAL目录不存在'}
        
        transaction_stats = {}
        operation_counts = {}
        total_records = 0
        
        try:
            wal_files = sorted([f for f in os.listdir(wal_dir) if f.endswith('.log')])
            
            for wal_file in wal_files:
                file_path = os.path.join(wal_dir, wal_file)
                records = self.read_wal_file(file_path)
                total_records += len(records)
                
                for record in records:
                    tx_id = record['transaction_id']
                    operation = record['operation']
                    
                    # 统计事务
                    if tx_id not in transaction_stats:
                        transaction_stats[tx_id] = {
                            'operations': [],
                            'start_time': record['timestamp'],
                            'end_time': record['timestamp'],
                            'status': 'unknown'
                        }
                    
                    transaction_stats[tx_id]['operations'].append(operation)
                    transaction_stats[tx_id]['end_time'] = record['timestamp']
                    
                    if operation in ['COMMIT', 'ROLLBACK']:
                        transaction_stats[tx_id]['status'] = operation.lower()
                    
                    # 统计操作类型
                    operation_counts[operation] = operation_counts.get(operation, 0) + 1
            
            return {
                'total_records': total_records,
                'total_transactions': len(transaction_stats),
                'operation_counts': operation_counts,
                'transactions': transaction_stats
            }
            
        except Exception as e:
            return {'error': str(e)}


def main():
    """主函数"""
    parser = argparse.ArgumentParser(description='WAL文件查看工具')
    parser.add_argument('database_path', help='数据库目录路径')
    parser.add_argument('--summary', action='store_true', help='显示WAL摘要')
    parser.add_argument('--detail', help='显示特定WAL文件的详细记录')
    parser.add_argument('--recent', type=int, help='显示最近N条记录')
    parser.add_argument('--operation', help='过滤特定操作类型 (BEGIN/COMMIT/INSERT/UPDATE/DELETE)')
    parser.add_argument('--analyze', action='store_true', help='分析事务统计')
    
    args = parser.parse_args()
    
    if not os.path.exists(args.database_path):
        print(f"错误: 数据库路径不存在: {args.database_path}")
        return 1
    
    reader = WALReader()
    
    try:
        if args.summary:
            reader.print_wal_summary(args.database_path)
        elif args.detail:
            reader.print_wal_records(args.detail, args.recent, args.operation)
        elif args.analyze:
            stats = reader.analyze_transactions(args.database_path)
            if 'error' in stats:
                print(f"分析失败: {stats['error']}")
            else:
                print(f"\n=== 事务分析结果 ===")
                print(f"总记录数: {stats['total_records']}")
                print(f"总事务数: {stats['total_transactions']}")
                print(f"\n操作统计:")
                for op, count in stats['operation_counts'].items():
                    print(f"  {op}: {count}")
        else:
            # 默认显示摘要
            reader.print_wal_summary(args.database_path)
    
    except Exception as e:
        print(f"执行失败: {e}")
        return 1
    
    return 0


if __name__ == "__main__":
    exit(main())
